# -*- coding: utf-8 -*-
import logging
from datetime import datetime, timedelta

from common.bankcard.handler import today_cash_flow_alert
from common.cache import redis_cache
from common.order import db as order_db
from common.order.db import modify_order_status_to_manual_success
from common.order.model import PAY_STATUS
from common.bankcard import db as bankcard_db
from common.bank_record import db as bankrecord_db
from common.bankcard import handler as bank_handler
from common.mch import handler as mch_handler
from common.utils import tz
from common.utils import exceptions as err
from common.utils.weight_sample import weight_sample

_LOGGER = logging.getLogger('hydra')


def _choose_bankcard(available_bankcards):
    # choose one
    candidates = []
    for bankcard in available_bankcards:
        if _check_bankcard_daily_amount(bankcard.id, bankcard.daily_recharge_limit):
            continue
        if check_bank_card_daily_time(bankcard.opening_time, bankcard.closing_time):
            continue
        candidates.append(bankcard)
    if not candidates:
        _LOGGER.warn('mch bankcard not available')
        raise err.PermissionError(u'没有可选银行卡')

    # add weight
    c_dict = dict([(i, c.weight) for i, c in enumerate(candidates)])
    chosed_index = weight_sample(c_dict.keys(), c_dict, 1).next()
    return candidates[chosed_index]


def check_bank_card_daily_time(opening_time, closing_time):
    try:
        hour = tz.local_now().hour
        if opening_time > closing_time:  # 跨日
            if hour > closing_time:
                closing_time += 24
            else:
                closing_time += 24
                hour += 24
        if hour < opening_time or hour >= closing_time:
            return True
    except Exception as e:
        _LOGGER.exception('check_bank_card_daily_time exceptions, %s', e)
        return True
    return False


def _check_bankcard_daily_amount(bankcard_id, daily_amount):
    # 检查累积充值风控
    daily_stats = redis_cache.get_bankcard_amount(bankcard_id)
    if int(daily_stats.get('amount', 0)) >= daily_amount:
        return True
    else:
        return False


def create_charge(charge_req):
    mch_id = charge_req['mch_id']
    pay_type = charge_req['pay_type']
    available_bankcards = bankcard_db.get_vaild_bankcard(mch_id, pay_type)
    if not available_bankcards:
        raise err.PermissionError(u'mch bankcard is empty')

    total_fee = int(float(charge_req['total_fee']))
    bankcard = _choose_bankcard(available_bankcards)
    bankcard_id = bankcard.id
    alipay_id = bankcard.alipay_id

    real_amount = redis_cache.get_bank_card_pay_amount(bankcard.name, total_fee)
    if real_amount is None:
        raise err.PermissionError(u'mch bankcard %s amount %s run out' % (bankcard.name, total_fee))

    today_cash_flow_alert(bankcard, "status.host")

    # create pay order
    pay = order_db.create_order(charge_req, bankcard.name, bankcard.account_number, real_amount)

    # submit pay order to channel
    url = bank_handler.get_bankcard_pay_url(bankcard_id, real_amount)
    return {'pay_url': url, 'real_amount': real_amount, 'order_id': pay.id, 'alipay_id': alipay_id, 'total_fee': total_fee}


def get_order(mch_id, out_trade_no):
    order = order_db.get_mch_order(mch_id, out_trade_no)
    return {
        'out_trade_no': order.out_trade_no,
        'total_fee': order.total_fee,
        'real_amount': order.real_amount,
        'pay_result': PAY_STATUS.SUCC if order.status == PAY_STATUS.MANUAL_SUCC else order.status,
        'extra': order.extra,
        'trade_no': order.id,
        'payed_at': tz.utc_to_local_str(order.payed_at) if order.payed_at else None,
    }


def manual_record_order(record_id, order_id, msg, operator):
    record = bankrecord_db.get_bank_record(record_id)
    if record and order_db.add_special_order_success(order_id, record_id, record.bankcard_name, record.amount, msg):
        mch_handler.notify_mch(order_id)
        bankrecord_db.match_record(record_id, order_id)
        modify_order_status_to_manual_success(order_id, operator)


def retry_push_failed():
    try:
        orders = order_db.get_notified_failed_orders()
        for order in orders:
            count = order.notify_failed_retry
            MIN = 5 if count == 0 else 10

            if order.notified_at + timedelta(minutes=MIN) <= datetime.utcnow():
                _LOGGER.info('order retry_push_failed id[%s] executed[%s] - start', order.id, MIN)
                mch_handler.notify_mch(order.id)
                params = {'notify_failed_retry': order.notify_failed_retry+1}
                order_db.upsert_order(params, int(order.id))
                _LOGGER.info('order retry_push_failed id[%s] executed[%s] - end', order.id, MIN)        
    except Exception as e:
        _LOGGER.exception('order retry_push_failed error %s', e)
